#include "PlayIPSound.h"

using namespace ipsound;

PlayIPSound::PlayIPSound( MutexBuffer* buffer, HWND window )
:buffer( buffer )
,soundBuf8( 0 )
,soundBufOffset( 0 )
,channelsNum( 1 )
,bitsPerSample( 16 )
,samplesPerSec( 8000 )
{
	LPDIRECTSOUND8 sound = 0;
	DirectSoundCreate8( 0, &sound, 0);
	sound->SetCooperativeLevel( window, DSSCL_PRIORITY );

	InitWaveFormat();

	memset( &SoundBufDesc, 0, sizeof(DSBUFFERDESC ) );
	SoundBufDesc.dwSize = sizeof(DSBUFFERDESC);
	SoundBufDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCHARDWARE;
	SoundBufDesc.dwBufferBytes = 2 * wfx.nAvgBytesPerSec;
	SoundBufDesc.lpwfxFormat = &wfx;

	LPDIRECTSOUNDBUFFER soundBuf = 0;
	sound->CreateSoundBuffer( &SoundBufDesc, &soundBuf, 0 );

	soundBuf->QueryInterface( IID_IDirectSoundBuffer8, (void**) &soundBuf8);
	soundBuf->Release();
}

void* PlayIPSound::Thread()
{
	LPDIRECTSOUNDNOTIFY8 notify = 0;
	HANDLE bufEvent = CreateEvent( 0, true, false, 0);
	DSBPOSITIONNOTIFY  notifyPos[2];

	soundBuf8->QueryInterface(IID_IDirectSoundNotify8, (void**)&notify);

	notifyPos[0].dwOffset = (SoundBufDesc.dwBufferBytes / 2) - 1;
	notifyPos[0].hEventNotify = bufEvent;

	notifyPos[1].dwOffset = SoundBufDesc.dwBufferBytes - 1;
	notifyPos[1].hEventNotify = bufEvent;

	notify->SetNotificationPositions( 2, notifyPos );
	notify->Release();

	void* initData = 0;
	unsigned long  initDataLength = 0;
	void* silence = malloc( SoundBufDesc.dwBufferBytes );
	memset( silence, 128, SoundBufDesc.dwBufferBytes );
	soundBuf8->Lock( 0, 0, &initData, &initDataLength, NULL, NULL, DSBLOCK_ENTIREBUFFER );
	memcpy(initData, silence, initDataLength);
	soundBuf8->Unlock( initData, initDataLength, 0, 0 );
	free(silence);
	
	void* intermedBuf = malloc( samplesPerSec * bitsPerSample / 8 );
	size_t read = buffer->Read( intermedBuf, samplesPerSec * bitsPerSample / 8 );
	PutDataInSoundBuffer( intermedBuf, read );
	
	soundBuf8->Play( 0, 0, DSBPLAY_LOOPING );
	ThreadStarted();

	while( true )
	{
		HRESULT result = WaitForSingleObject( bufEvent, INFINITE );

		size_t read = buffer->Read( intermedBuf, samplesPerSec * bitsPerSample / 8 );
		PutDataInSoundBuffer( intermedBuf, read );

		ResetEvent( bufEvent );
	}

	free( intermedBuf );
}

PlayIPSound::~PlayIPSound()
{
	soundBuf8->Release();
}

void PlayIPSound::InitWaveFormat()
{
	memset( &wfx, 0, sizeof(WAVEFORMATEX) );
	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.nChannels = channelsNum;
	wfx.nSamplesPerSec = samplesPerSec;
	wfx.wBitsPerSample = bitsPerSample;
	wfx.nBlockAlign = (wfx.wBitsPerSample * wfx.nChannels) / 8;
	wfx.nAvgBytesPerSec = samplesPerSec * wfx.nBlockAlign;
}

void PlayIPSound::PutDataInSoundBuffer( const void* data, const size_t& size )
{
	void*  lockedDataPtr1 = 0;
	unsigned long lockedDataSize1 = 0;
	void*  lockedDataPtr2 = 0;
	unsigned long lockedDataSize2 = 0;

	if ( soundBuf8->Lock(soundBufOffset, size, &lockedDataPtr1, &lockedDataSize1, &lockedDataPtr2, &lockedDataSize2, 0) == DSERR_BUFFERLOST )
	{
		soundBuf8->Restore();
		soundBuf8->Lock(soundBufOffset, size, &lockedDataPtr1, &lockedDataSize1, &lockedDataPtr2, &lockedDataSize2, 0);
	}

	if(!lockedDataPtr2)
	{
		memcpy(lockedDataPtr1, data, size);
	}
	else
	{
		memcpy(lockedDataPtr1, data, size - lockedDataSize2);
		memcpy(lockedDataPtr2, (char*)data + size - lockedDataSize2, lockedDataSize2);
	}

	soundBuf8->Unlock(lockedDataPtr1, lockedDataSize1, lockedDataPtr2, lockedDataSize2);

	soundBufOffset += lockedDataSize1;
	soundBufOffset %= SoundBufDesc.dwBufferBytes;
	soundBufOffset += lockedDataSize2;
	soundBufOffset %= SoundBufDesc.dwBufferBytes;
}
